﻿using Skywave.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace Skywave.Net
{
    public class WebApiClient
    {
        string _ServiceURL = "";

        public WebApiClient(string serviceUrl)
        {
            if (!_ServiceURL.EndsWith("/"))
                _ServiceURL = _ServiceURL + "/";
            _ServiceURL = serviceUrl;
        }

        //public object Transport_Api(string apiName)
        //{
        //    return new
        //    {
        //        url = Url_Api(apiName),
        //        headers = new
        //        {
        //            Authorization = Request_AuthorizationHeaderValue.ToString()
        //        },
        //    };
        //}

        public string Url_Api(string apiName, bool includeHostAddress = true)
        {
            string r = "";
            if (includeHostAddress)
                r += _ServiceURL;
            r += string.Format("api/{0}", apiName);
            return r;
        }

        private AuthenticationHeaderValue _Request_AuthorizationHeaderValue = null;
        /// <summary>
        /// ...
        /// </summary>
        public AuthenticationHeaderValue Request_AuthorizationHeaderValue
        {
            get { return _Request_AuthorizationHeaderValue; }
            set { _Request_AuthorizationHeaderValue = value; }
        }
        public string Request_AuthorizationHeaderValue_Text
        { get { return (Request_AuthorizationHeaderValue == null) ? "" : Request_AuthorizationHeaderValue.ToString(); } }


        public async Task<TaskRunResult<TResult>> InvokeApi<TResult>(string apiName)
        {
            return await Post<TResult>(Url_Api(apiName, false));
        }
        public async Task<TaskRunResult<bool>> InvokeApi<TInput>(string apiName, TInput arg)
        {
            return await Post<TInput>(Url_Api(apiName, false), arg);
        }
        public async Task<TaskRunResult<TResult>> InvokeApi<TResult, TInput>(string apiName, TInput arg)
        {
            return await Post<TResult, TInput>(Url_Api(apiName, false), arg);
        }
        public async Task<TaskRunResult<string>> InvokeApi_Native<TInput>(string apiName, TInput arg)
        {
            return await _Post<string, TInput>(Url_Api(apiName, false), arg, true, true, true);
        }

        public async Task<TaskRunResult<TResult>> PostHttpContent<TResult>(string path, HttpContent content)
        {
            return await _Post<TResult, HttpContent>(path, content, true);
        }
        public async Task<TaskRunResult<bool>> PostHttpContent(string path, HttpContent content)
        {
            var r_Post = await _Post<bool, HttpContent>(path, content, false);
            TaskRunResult<bool> r = new TaskRunResult<bool>(r_Post.IsSuccess, r_Post.Exception, r_Post.Canceled);
            return r;
        }
        public async Task<TaskRunResult<bool>> Post<TInput>(string path, TInput arg)
        {
            var r_Post = await _Post<bool, TInput>(path, arg, false);
            TaskRunResult<bool> r = new TaskRunResult<bool>(r_Post.IsSuccess, r_Post.Exception, r_Post.Canceled);
            return r;
        }
        public async Task<TaskRunResult<TResult>> Post<TResult>(string path)
        {
            return await _Post<TResult, bool?>(path, null, true, hasArg: false);
        }
        public async Task<TaskRunResult<TResult>> Post<TResult, TInput>(string path, TInput arg)
        {
            return await _Post<TResult, TInput>(path, arg, true);
        }

        private async Task<TaskRunResult<TResult>> _Post<TResult, TInput>(string path, TInput arg, bool hasReturn, bool hasArg = true, bool returnContentAsString = false)
        {
            TaskRunResult<TResult> r;
            try
            {
                using (var client = new HttpClient())
                {
                    // TODO - Send HTTP requests
                    //string.Format("{0}{1}", _ServiceURL, invokePath)
                    client.BaseAddress = new Uri(_ServiceURL);
                    client.DefaultRequestHeaders.Accept.Clear();
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    if (Request_AuthorizationHeaderValue != null)
                        client.DefaultRequestHeaders.Authorization = Request_AuthorizationHeaderValue;

                    // New code:
                    HttpResponseMessage response;
                    if (hasArg)
                    {
                        if (typeof(TInput) == typeof(HttpContent))
                            response = await client.PostAsync(path, arg as HttpContent);
                        else
                            response = await client.PostAsJsonAsync<TInput>(path, arg);
                    }
                    else
                        response = await client.PostAsync(path, null);

                    if (!response.IsSuccessStatusCode)
                    {
                        var ex1 = new System.Net.WebException(response.StatusCode.ToString());
                        ex1.Data.Add("content", await response.Content.ReadAsStringAsync());
                        r = new TaskRunResult<TResult>(default(TResult), ex1);
                    }
                    else
                    {
                        TResult r_InPost = default(TResult);
                        if (hasReturn)
                        {
                            if (returnContentAsString)
                                r_InPost = (TResult)((object)await response.Content.ReadAsStringAsync());
                            else
                                r_InPost = await response.Content.ReadAsAsync<TResult>();
                        }
                        r = new TaskRunResult<TResult>(r_InPost);
                    }
                }
            }
            catch (Exception ex1)
            {
                r = new TaskRunResult<TResult>(default(TResult), ex1);
            }
            return r;
        }
    }
}
